﻿namespace Stopwatch.Core.Persistence
{
    using System;
    using System.Threading;
    using Newtonsoft.Json;

    class StopTimer : ITimer
    {
        const int PERIOD = 10;

        #region ITimer properties

        public Status Status { get; set; }

        public Time Duration { get; set; }

        public Time Runtime { get; set; }

        public Time WarningThreshold { get; set; }

        public DateTime Beginning { get; set; }

        public DateTime Pausing { get; set; }

        #endregion

        [JsonIgnore]
        Timer timer;

        public StopTimer()
        {
            timer = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite);
        }

        void TimerCallback(object state)
        {
            // got the runtime
            var run = Duration - (DateTime.Now - Beginning).ToShortTime();

            if (Runtime == run) { return; }

            Runtime = run;

            if (OnTick != null)
            {
                OnTick(this, EventArgs.Empty);
            }

            if (OnWarning != null && Runtime == WarningThreshold)
            {
                OnWarning(this, EventArgs.Empty);
            }

            if (Runtime <= Time.Zero)
            {
                Reset();

                if (OnCompleted != null)
                {
                    OnCompleted(this, EventArgs.Empty);
                }
            }
        }

        #region operations

        public bool Start()
        {
            if (Duration <= Time.Zero) { return false; }

            switch (Status)
            {
                case Status.Paused:
                    Beginning += (DateTime.Now - Pausing);
                    break;

                case Status.Ready:
                    Beginning = DateTime.Now;
                    break;
            }

            timer.Change(PERIOD, PERIOD);
            Status = Status.Started;

            return true;
        }

        public void Pause()
        {
            timer.Change(Timeout.Infinite, Timeout.Infinite);
            Pausing = DateTime.Now;

            Status = Status.Paused;

            if (OnPaused != null)
            {
                OnPaused(this, EventArgs.Empty);
            }
        }

        public void Reset()
        {
            timer.Change(Timeout.Infinite, Timeout.Infinite);

            Runtime = Time.Zero;
            Status = Status.Ready;
        }

        #endregion

        #region events

        public event EventHandler OnPaused;

        public event EventHandler OnCompleted;

        public event EventHandler OnTick;

        public event EventHandler OnWarning;

        #endregion

        #region member of IDispose

        public void Dispose()
        {
            if (timer != null)
            {
                timer.Dispose();
                timer = null;
            }
        }

        #endregion
    }
}
